
import { EventEmitter } from '../common/event-emitter';
import { ThreadPool } from '../thread/thread-pool';
import {  OptimalThreadCountOptions } from '../common/helpers';
import { readBlobAsArrayBuffer } from '../fs/helpers';
import CryptoJS from 'crypto-js';
import { HashAlgorithm } from './types';

/**
 * 文件哈希计算选项
 */
export interface FileHashOptions {
  /** 哈希算法 */
  algorithm: HashAlgorithm;
  /** 分块大小 (字节)，默认 1MB */
  chunkSize?: number;
  /** 是否使用多线程，默认true */
  useThreads?: boolean;
}

/**
 * 哈希计算进度信息
 */
export interface HashProgress {
  /** 当前进度 (0-100) */
  progress: number;
  /** 已处理字节数 */
  processedBytes: number;
  /** 总字节数 */
  totalBytes: number;
}

export enum CryptoEvent {
  /** 哈希计算进度事件 */
  HashProgress = 't1h0.crypto.MultiThreadCrypto.event.hashProgress',
  /** 哈希计算完成事件 */
  HashComplete = 't1h0.crypto.MultiThreadCrypto.event.hashComplete'
}

/**
 * 加密工具类，提供字符串和文件的哈希计算功能
 * 支持多线程处理大型文件，提供进度事件通知
 * @class MultiThreadCrypto
 * @extends EventEmitter
 */
export class MultiThreadCrypto extends EventEmitter {
  /**
   * 线程池实例，用于并行处理文件分片
   * @private
   * @type {ThreadPool | null}
   */
  private __threadPool: ThreadPool | null = null;

  /**
   * 线程池大小或配置选项
   * @private
   * @type {number | OptimalThreadCountOptions}
   */
  private __threadCount: number | OptimalThreadCountOptions = 0;

  /**
   * 构造函数
   * @param {number | OptimalThreadCountOptions} threadCount - 线程池大小，为0则自动获取最优线程数
   */
  constructor(threadCount: number = 0) {
    super();
    this.__threadCount = threadCount || {
      taskType: 'cpu'
    };
  }

  /**
   * 获取线程池实例（懒加载）
   * @private
   * @returns {ThreadPool} 线程池实例
   */
  private __getThreadPool(): ThreadPool {
    if (!this.__threadPool) {
      // 初始化线程池，指定worker路径
      this.__threadPool = new ThreadPool(
        new URL('../thread/workers/blobToBufferWorker.ts', import.meta.url).href,
        this.__threadCount);

      // 监听线程池事件，转发进度事件
      // this.__threadPool.on(ThreadPoolEvent.TaskProgress, (data: any) => {
      //   if (data.taskType === 'fileHash') {
      //     this.emit(CryptoEvent.HashProgress, {
      //       progress: data.progress,
      //       processedBytes: data.processedBytes,
      //       totalBytes: data.totalBytes
      //     });
      //   }
      // });
    }
    return this.__threadPool;
  }

  /**
   * 计算字符串哈希值
   * @public
   * @param {string} str - 输入字符串
   * @param {HashAlgorithm} algorithm - 哈希算法
   * @returns {string} 哈希值（十六进制字符串）
   * @throws {Error} 当输入参数不是字符串时抛出错误
   * @throws {Error} 当指定了不支持的哈希算法时抛出错误
   */
  public stringHash(str: string, algorithm: HashAlgorithm): string {
    if (typeof str !== 'string') {
      throw new Error(`输入参数必须是字符串，当前类型：${typeof str}`);
    }

    // 使用crypto-js计算哈希
    let hash: CryptoJS.lib.WordArray;
    switch (algorithm) {
      case HashAlgorithm.MD5:
        hash = CryptoJS.MD5(str);
        break;
      case HashAlgorithm.SHA1:
        hash = CryptoJS.SHA1(str);
        break;
      case HashAlgorithm.SHA256:
        hash = CryptoJS.SHA256(str);
        break;
      case HashAlgorithm.SHA512:
        hash = CryptoJS.SHA512(str);
        break;
      default:
        throw new Error(`不支持的哈希算法: ${algorithm}`);
    }

    return hash.toString(CryptoJS.enc.Hex);
  }

  /**
   * 计算小数据哈希值（适用于内存可容纳的小型文件或数据）
   * @public
   * @param {File|Blob} data - 输入数据（File或Blob对象）
   * @param {HashAlgorithm} algorithm - 哈希算法
   * @returns {Promise<string>} 哈希值（十六进制字符串）
   * @throws {Error} 当输入数据类型不支持时抛出错误
   */
  public async smallDataHash(data: File | Blob, algorithm: HashAlgorithm): Promise<string> {
    let arrayBuffer: ArrayBuffer;
    if (data instanceof Blob) {
      arrayBuffer = await readBlobAsArrayBuffer({ blob: data });
    } else {
      throw new Error(`不支持的数据类型：${typeof data}`);
    }

    return this.smallDataHashFromArrayBuffer(arrayBuffer, algorithm);
  }

  /**
   * 从ArrayBuffer计算哈希值
   * @public
   * @param {ArrayBuffer} arrayBuffer - 输入的ArrayBuffer数据
   * @param {HashAlgorithm} algorithm - 哈希算法
   * @returns {string} 哈希值（十六进制字符串）
   * @throws {Error} 当指定了不支持的哈希算法时抛出错误
   */
  public smallDataHashFromArrayBuffer(arrayBuffer: ArrayBuffer, algorithm: HashAlgorithm): string {
    // 将ArrayBuffer转换为WordArray
    const wordArray = CryptoJS.lib.WordArray.create(new Uint8Array(arrayBuffer));
    // 计算哈希
    let hash: CryptoJS.lib.WordArray;
    switch (algorithm) {
      case HashAlgorithm.MD5:
        hash = CryptoJS.MD5(wordArray);
        break;
      case HashAlgorithm.SHA1:
        hash = CryptoJS.SHA1(wordArray);
        break;
      case HashAlgorithm.SHA256:
        hash = CryptoJS.SHA256(wordArray);
        break;
      case HashAlgorithm.SHA512:
        hash = CryptoJS.SHA512(wordArray);
        break;
      default:
        throw new Error(`不支持的哈希算法: ${algorithm}`);
    }
    return hash.toString(CryptoJS.enc.Hex);
  }

  /**
   * 计算文件哈希值（支持大文件分块处理）
   * @public
   * @param {File|Blob} file - 输入文件（File或Blob对象）
   * @param {FileHashOptions} options - 哈希计算选项
   * @param {HashAlgorithm} options.algorithm - 哈希算法
   * @param {number} [options.chunkSize=1024*1024] - 分块大小，默认1MB
   * @param {boolean} [options.useThreads=true] - 是否使用多线程，默认true
   * @returns {Promise<string>} 哈希值（十六进制字符串）
   * @fires CryptoEvent.HashProgress - 哈希计算进度事件
   */
  public async fileHash(file: File | Blob, options: FileHashOptions): Promise<string> {
    const { algorithm, chunkSize = 1024 * 1024, useThreads = true } = options;

    if (!useThreads) {
      // 单线程计算哈希
      return await this.smallDataHash(file, algorithm);
    }
    // 使用多线程计算
    return this.__calculateFileHashWithThreads(file, algorithm, chunkSize);
  }

  /**
   * 使用多线程计算文件哈希的内部实现
   * @private
   * @param {File|Blob} file - 输入文件
   * @param {HashAlgorithm} algorithm - 哈希算法
   * @param {number} chunkSize - 分块大小
   * @returns {Promise<string>} 哈希值
   */
  private async __calculateFileHashWithThreads(
    file: File | Blob,
    algorithm: HashAlgorithm,
    chunkSize: number
  ): Promise<string> {
    const fileSize = file.size;
    const chunks = Math.ceil(fileSize / chunkSize);
    let processedBytes = 0;

    // 创建主哈希对象
    let hashObj: any;
    switch (algorithm) {
      case HashAlgorithm.MD5:
        hashObj = CryptoJS.algo.MD5.create();
        break;
      case HashAlgorithm.SHA1:
        hashObj = CryptoJS.algo.SHA1.create();
        break;
      case HashAlgorithm.SHA256:
        hashObj = CryptoJS.algo.SHA256.create();
        break;
      case HashAlgorithm.SHA512:
        hashObj = CryptoJS.algo.SHA512.create();
        break;
      default:
        throw new Error(`不支持的哈希算法: ${algorithm}`);
    }

    // 创建结果数组存储每个块的ArrayBuffer
    const arrayBuffers = new Map<number, ArrayBuffer>();
    let completedChunks = 0;
    let nextExpectedIndex = 0;  // 下一个期望处理的分片索引
    let isAllChunksProcessed = false;

    // 使用Promise来等待所有块处理完成
    return new Promise<string>((resolve, reject) => {
      // 分块处理文件
      const tasks = [];
      for (let i = 0; i < chunks; i++) {
        const start = i * chunkSize;
        const end = Math.min(start + chunkSize, fileSize);

        // 创建文件分片
        const blobSlice = file.slice(start, end);

        // 准备任务数据
        tasks.push({
          data: {
            blob: blobSlice,
            // 传递块索引用于排序
            index: i
          },
          resolve: async (result: { arrayBuffer: ArrayBuffer, index: number }) => {
            try {
              const { arrayBuffer, index } = result;
              // 存储分片数据
              arrayBuffers.set(index, arrayBuffer);
              completedChunks++;

              // 尝试按顺序处理已完成的分片
              processNextAvailableChunks();

              // 检查是否所有分片都已处理完成
              if (completedChunks === chunks) {
                isAllChunksProcessed = true;
                // 确保最后一个分片也被处理
                processNextAvailableChunks();
              }
            } catch (error) {
              reject(error);
            }
          },
          reject,
          retries: 0,
          maxRetries: 3
        });
      }

      // 使用批量提交任务
      this.__getThreadPool().addBatchTasks(tasks);

      const _self = this;
      // 按顺序处理可用的分片
      function processNextAvailableChunks() {
        // 当还有期望的分片且该分片已完成时
        while (nextExpectedIndex < chunks && arrayBuffers.has(nextExpectedIndex)) {
          // 更新主哈希
          const chunk = arrayBuffers.get(nextExpectedIndex)!;
          // 使用分片的ArrayBuffer更新主哈希
          const wordArray = CryptoJS.lib.WordArray.create(new Uint8Array(chunk));
          hashObj.update(wordArray);
          // 触发进度事件
          // 使用实际处理的字节数更新进度
          // 确保processedBytes不会超过文件总大小
          processedBytes = Math.min((nextExpectedIndex + 1) * chunkSize, fileSize);
          const progress = (processedBytes / fileSize) * 100;
          _self.emit(CryptoEvent.HashProgress, {
            progress,
            processedBytes,
            totalBytes: fileSize
          });
          nextExpectedIndex++;
        }

        // 如果所有分片都已完成且所有分片都已按顺序处理
        if (isAllChunksProcessed && nextExpectedIndex === chunks) {
          // 获取最终哈希值
          const finalHash = hashObj.finalize();
          // 触发完成事件
          _self.emit(CryptoEvent.HashComplete, finalHash.toString(CryptoJS.enc.Hex));
          resolve(finalHash.toString(CryptoJS.enc.Hex));
        }
      }
    });
  }

  /**
   * 销毁线程池，释放资源
   * @public
   * @returns {void}
   */
  public destroy(): void {
    if (this.__threadPool) {
      this.__threadPool.destroy();
      this.__threadPool = null;
    }
  }
}